#ifndef _I_COMMON_H
#define _I_COMMON_H
#include <cmath>
#include "tinyxml.h"
namespace raytracer
{
	class vector3
	{
	public:
		vector3() : x( 0.0f ), y( 0.0f ), z( 0.0f ) {};
		vector3( float a_X, float a_Y, float a_Z ) : x( a_X ), y( a_Y ), z( a_Z ) {};
		void Set( float a_X, float a_Y, float a_Z ) { x = a_X; y = a_Y; z = a_Z; }
		void Normalize() { float l = 1.0f / Length(); x *= l; y *= l; z *= l; }
		float Length() const{ return (float)sqrt( x * x + y * y + z * z ); }
		float SqrLength() const{ return x * x + y * y + z * z; }
		float Dot( const vector3& a_V ) const{ return x * a_V.x + y * a_V.y + z * a_V.z; }
		vector3 Cross( const vector3& b )const { return vector3( y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x ); }
		void operator += ( vector3& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
		void operator += ( vector3* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
		void operator -= ( vector3& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
		void operator -= ( vector3* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
		void operator *= ( float f ) { x *= f; y *= f; z *= f; }
		void operator *= ( vector3& a_V ) { x *= a_V.x; y *= a_V.y; z *= a_V.z; }
		void operator *= ( vector3* a_V ) { x *= a_V->x; y *= a_V->y; z *= a_V->z; }
		vector3 operator- () const { return vector3( -x, -y, -z ); }
		friend vector3 operator + ( const vector3& v1, const vector3& v2 ) { return vector3( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z ); }
		friend vector3 operator - ( const vector3& v1, const vector3& v2 ) { return vector3( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z ); }
		friend vector3 operator + ( const vector3& v1, vector3* v2 ) { return vector3( v1.x + v2->x, v1.y + v2->y, v1.z + v2->z ); }
		friend vector3 operator - ( const vector3& v1, vector3* v2 ) { return vector3( v1.x - v2->x, v1.y - v2->y, v1.z - v2->z ); }
		friend vector3 operator * ( const vector3& v, float f ) { return vector3( v.x * f, v.y * f, v.z * f ); }
		friend vector3 operator * ( const vector3& v1, const vector3& v2 ) { return vector3( v1.x * v2.x, v1.y * v2.y, v1.z * v2.z ); }
		friend vector3 operator * ( float f, const vector3& v ) { return vector3( v.x * f, v.y * f, v.z * f ); }
		union
		{
			struct { float x, y, z; };
			struct { float r, g, b; };
			struct { float cell[3]; };
		};
	};

	class Ray
	{
	public:
		Ray() :mOrigin( vector3( 0, 0, 0 ) ), mDir( vector3( 0, 0, 1 ) ) {};
		Ray( const vector3& a_Origin, const vector3& a_Dir ){SetOrigin(a_Origin);SetDirection(a_Dir);};
		void SetOrigin( const vector3& a_Origin ) { mOrigin = a_Origin; }
		void SetDirection(const vector3& a_Direction ) { mDir = a_Direction;mDir.Normalize(); }
		const vector3& GetOrigin()const { return mOrigin; }
		const vector3& GetDirection()const { return mDir; }
		//void NormalizeDirection(){mDir.Normalize();}
	private:
		vector3 mOrigin;
		vector3 mDir;
	};

	class Material
	{
	private:
		//arguments for illumination equations
		vector3 mDiffuse;
		float mAmbnRef,mDiffRef,mSpecRef,mRefrRef;
		int mSpecPow;
		//reflection index
		float mReflIdx;
		//refraction index
		float mRefrIdx;
	public:
		Material():mDiffuse(),mAmbnRef(0),mDiffRef(0),mSpecRef(0),mRefrRef(0),mSpecPow(1),mReflIdx(0),mRefrIdx(0){}
		void Set(const TiXmlElement* ele);
		void SetDiffuse(const vector3& diffuse){mDiffuse=diffuse;}
		const vector3& GetDiffuse()const{return mDiffuse;}
		void SetReflection(float reflection){mReflIdx=reflection;}
		float GetReflection()const{return mReflIdx;}
		void SetRefraction(float refraction){mRefrRef=refraction;}
		float GetRefraction()const{return mRefrRef;}
		void SetRefractionIndex(float value){mRefrIdx=value;}
		float GetRefractionIndex()const{return mRefrIdx;}
		void SetAmbientReflection(float value){mAmbnRef=value;}
		float GetAmbientReflection()const{return mAmbnRef;}
		void SetDiffuseReflection(float value){mDiffRef=value;}
		float GetDiffuseReflection()const{return mDiffRef;}
		void SetSpecular(float value){mSpecRef=value;}
		float GetSpecular()const{return mSpecRef;}
		void SetSpecularPower(int value){mSpecPow=value;}
		int GetSpecularPower()const{return mSpecPow;}		
	};
}
#endif